/*
 * Copyright (c) 2010-2013 Ruben Chakhmakhchyan. All rights reserved.
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Eclipse Public License
 * Version 1.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://opensource.org/licenses/EPL-1.0 and read it before using this file.
 */

#include "plugin_model_impl.h"


/*
|	 	0	1	2	3	4	5	6
---------------------------------
.|	.	a	N	,	:	-	o	e
0|	>	1	2	-	-	-	-	-	// entry point
1|	a	-	-	-	-	-	-	-
2|	N	-	2	3	4	5	-	-
3|	,	-	2	-	-	-	-	-
4|	:	-	-	-	-	-	6	7
5|	-	-	8	-	-	-	-	-
6|	o	-	-	3	-	-	-	-
7|	e	-	-	3	-	-	-	-
8|	r	-	-	3	4	-	-	-	// range


> - entry point, a - 'all', N - numeric, o - 'odd', e = 'even', r - range
*/

int g_stateTable[][ 7 ] = 
{
    { 1, 2, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0 },
    { 0, 2, 3, 4, 5, 0, 0 },
    { 0, 2, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 6, 7 },
    { 0, 8, 0, 0, 0, 0, 0 },
    { 0, 0, 3, 0, 0, 0, 0 },
    { 0, 0, 3, 0, 0, 0, 0 },
    { 0, 0,	3, 4, 0, 0, 0 },
};

struct SYMBOL
{
    int type;
    int value;
};

struct RANGE
{
    int from;
    int to;
    bool all;
    bool odd;
    bool even;

    RANGE* nextRange;
};

bool _PluginModel::parseIndices( const char* s, int*& indices, int& count )
{
    indices = NULL;

    bool result = true;
    char c = 0;
    bool semicolon = false;
    int literalLength = 0;
    uint_t number = 0;
    int literalType = 0;
    bool isNumber = false;
    bool isAlpha = false;
    int symbolIndex = 0;
    int stateRow = 0;

    SYMBOL stack[] = { { 0 }, { 0 }, { 0 }, { 0 }, { 0 } };
    SYMBOL* sp = stack; // stack pointer
    int ops = 0;

    //RANGE startRange = { 0 };
    RANGE* rp = NULL; // range pointer
    RANGE* rs = NULL; // start range
    int rangeCount = 0;

    char literalBuffer[ 16 ] = { 0 };

    for ( int i = 0; s[ i ] != 0 && i < 1023; i++ )
    {
        c = s[ i ];

        bool emit = false;
        bool emitNumber = false;
        bool emitString = false;

        switch ( c )
        {
        case ' ': continue;
        case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
            if ( isAlpha )
                return false;
            isNumber = true;

            number = number * 10 + ( c - '0' );
            if ( number > MAXINT )
            {
                result = false;
                break; // syntax error
            }

            literalLength++;

            break;

        case 'a': case 'l': case 'o': case 'd': case 'e': case 'v': case 'n':
            if ( isNumber )
            {
                result = false;
                break; // syntax error
            }

            isAlpha = true;

            literalBuffer[ literalLength++ ] = c;

            break;

        case ',': case '-': case ':': // terminals

            emit = true;

            break;

        default:
            {
                result = false;
                break; // syntax error
            }
        }

        if ( !result )
        {
            break;
        }

        if ( literalLength == 16 ) // literal too long
        {
            result = false;
            break; // syntax error
        }

        bool end = ( i == 1023 || s[ i + 1 ] == 0 );
        char s1 = end ? 0 : s[ i + 1 ];
        bool nextIsTerminal = !end && ( s1 == ',' || s1 == ':' || s1 == '-' );
        if ( end || nextIsTerminal )
        {
            if ( isNumber ) emitNumber = true;
            if ( isAlpha ) emitString = true;

            isNumber = false;
            isAlpha = false;

            emit = true;
        }

        if ( emit )
        {
            if ( emitNumber )
                symbolIndex = 1;
            else if ( emitString )
            {
                if ( !strcmp( literalBuffer, "all" ) )
                    symbolIndex = 0;
                else if ( !strcmp( literalBuffer, "odd" ) || !strcmp( literalBuffer, "o" ) )
                    symbolIndex = 5;
                else if ( !strcmp( literalBuffer, "even" ) || !strcmp( literalBuffer, "e" ) )
                    symbolIndex = 6;
                else
                {
                    result = false;
                    break; // syntax error
                }
            }
            else if ( c == ',' )
                symbolIndex = 2;
            else if ( c == ':' )
                symbolIndex = 3;
            else if ( c == '-' )
                symbolIndex = 4;
            else
            {
                result = false;
                break; // syntax error
            }

            stateRow = g_stateTable[ stateRow ][ symbolIndex ];

            if ( stateRow == 0 && ( !end || !emit ) )
            {
                result = false;
                break;
            }

            if ( emitNumber )
            {
                sp->type = 1;
                sp->value = number;
                sp++;
                ops++;
            }
            else if ( symbolIndex == 5 || symbolIndex == 6 )
            {
                sp->type = symbolIndex;
                sp->value = 0;
                sp++;
                ops++;
            }
            if ( symbolIndex == 2 || end )
            {
                RANGE* range = new RANGE;
                memset( range, 0, sizeof( RANGE ) );

                switch ( ops )
                {
                case 0:
                    range->all = true;
                    break;
                case 1:
                    range->from = ( --sp )->value;
                    break;
                case 2:
                    range->to = ( --sp )->value;
                    range->from = ( --sp )->value;
                    break;
                case 3:
                    int type = ( --sp )->type;
                    range->odd = type == 5;
                    range->even = type == 6;

                    range->to = ( --sp )->value;
                    range->from = ( --sp )->value;
                    break;
                }

                if ( rp != NULL )
                {
                    rp->nextRange = range;
                }
                else
                {
                    rs = range;
                }

                rp = range;
                rangeCount++;

                sp = stack;
                ops = 0;
            }

            memset( literalBuffer, 0, 16 );
            literalLength = 0;
            number = 0;
        }
    }

    // generate the indices based on ranges

    if ( rp == NULL )
    {
        result = false;
    }
    else
    {
        rp = rs;

        indices = new int[ 256 ];
        memset( indices, 0, 256 * sizeof ( int ) );
        int* ci = indices;

        if ( result )
        {
            if ( rp->all )
            {
                indices = NULL;
            }
            else
            {
                do
                {
                    if ( rp->to == 0 )
                    {
                        indices[ count++ ] = rp->from;
                    }
                    else
                    {
                        int step = 1;

                        int from = 0, to = 0;
                        // set the direction and bounds
                        if ( rp->to > rp->from )
                        {
                            from = rp->from;
                            to = rp->to;
                        }
                        else
                        {
                            from = rp->to;
                            to = rp->from;
                            step = -step;
                        }

                        if ( rp->odd )
                        {
                            from = ( ( from + ( step > 0 ? 0 : -2 ) ) & ( ~1 ) ) + 1;
                            step = 2 * step;
                        }
                        else if ( rp->even )
                        {
                            from = ( ( from + ( step > 0 ? 1 : -1 ) ) & ( ~1 ) );
                            step = 2 * step;
                        }

                        for ( int i = from; ( step > 0 ? i <= to : i >= to ); i += step )
                        {
                            indices[ count++ ] = i;
                        }
                    }
                }
                while ( rp = rp->nextRange );
            }
        }
    }

    // cleanup range list
    rp = rs;
    RANGE* tmp = NULL;

    if ( rp )
    {
        do
        {
            tmp = rp->nextRange;

            delete rp;

            rp = tmp;
        }
        while ( rp );
    }

    return result;
}
